package il.ac.technion.wiki.impl;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import il.ac.technion.wiki.api.IContentRanker;
import il.ac.technion.wiki.api.IWikiEngine.IWikiContent;
import il.ac.technion.wiki.api.search.AndSearchExpression;
import il.ac.technion.wiki.api.search.ISearchExpression;
import il.ac.technion.wiki.api.search.NotSearchExpression;
import il.ac.technion.wiki.api.search.OrSearchExpression;
import il.ac.technion.wiki.api.search.TextSearchExpression;

public class WikiRanker implements IContentRanker{
	private Set<String> neutralWords = new HashSet<String>();
	@Override
	public int rank(IWikiContent content, ISearchExpression expr) {		
		QueryType qt = null;
		for (QueryType q: QueryType.values()){
			if (q.canParseQuery(expr)){
				qt = q;
				break;
			}
		}
		if (qt == null)
			throw new RuntimeException();
		return qt.evaluate(expr, content, neutralWords);
	}

	@Override
	public void setNeutralWords(String[] words) {
		for (String word: words){
			neutralWords.add(word);
		}
	}
	
	@Override
	public void removeNeutralWords(String[] words) {
		for (String word: words){
			neutralWords.remove(word);
		}
	}
	
	public enum QueryType{
		AND(AndSearchExpression.class) {			
			@Override
			int evaluate(ISearchExpression e, IWikiContent content, Set<String> neutralWords){
				AndSearchExpression andExp = (AndSearchExpression)e;
				QueryType left = null,right = null;
				for (QueryType q: QueryType.values()){
					if (q.canParseQuery(andExp.left))
						left = q;
					if (q.canParseQuery(andExp.right))
						right = q; 
				}
				if (left == null || right == null)
					throw new RuntimeException();
				return Math.min(left.evaluate(andExp.left, content, neutralWords), right.evaluate(andExp.right, content, neutralWords));
			}						
		},
		OR(OrSearchExpression.class) {
			@Override
			int evaluate(ISearchExpression e, IWikiContent content, Set<String> neutralWords){
				OrSearchExpression orExp = (OrSearchExpression)e;
				QueryType left = null,right = null;
				for (QueryType q: QueryType.values()){
					if (q.canParseQuery(orExp.left))
						left = q;
					if (q.canParseQuery(orExp.right))
						right = q; 
				}
				if (left == null || right == null)
					throw new RuntimeException();
				return left.evaluate(orExp.left, content, neutralWords) + right.evaluate(orExp.right, content, neutralWords);
			}
		},
		NOT(NotSearchExpression.class) {
			@Override
			int evaluate(ISearchExpression e, IWikiContent content, Set<String> neutralWords){
				NotSearchExpression notExp = (NotSearchExpression)e;				
				for (QueryType q: QueryType.values()){
					if (q.canParseQuery(notExp.exp))
						return -q.evaluate(notExp.exp, content, neutralWords);
				}
				throw new RuntimeException();
			}
		},
		EXPRESSION(TextSearchExpression.class) {			
			String removeNeutralWords(String text, Set<String> neutralWords){				
				String res = text;
				for (String word : neutralWords){
					res = res.replaceAll(Pattern.quote(word), "");
				}
				return res;	
			}
			
			String stripWhiteSpaces(String str){
				return str.replaceAll("\\s+", " ");
			}
			
			@Override
			int evaluate(ISearchExpression e, IWikiContent content, Set<String> neutralWords){	
				Pattern p = Pattern.compile(Pattern.quote(stripWhiteSpaces(removeNeutralWords(((TextSearchExpression)e).text, neutralWords))));
				Matcher m = p.matcher(stripWhiteSpaces(removeNeutralWords(content.getText().toLowerCase(), neutralWords)));
				int counter = 0;
				while(m.find())
					++counter;
				return counter;
			}
		};		
		
		Class<? extends ISearchExpression> c;
		QueryType(Class<? extends ISearchExpression> c){
			this.c = c; 
		}
		
		abstract int evaluate(ISearchExpression e, IWikiContent content, Set<String> neutralWords);
		
		boolean canParseQuery(ISearchExpression e) {
			return e.getClass().equals(this.c);
		}
		
		
	}
}
